#include "game.h"
#include "game_debug.h"
#include "game_render_group.h"
#include "game_entity.h"
#include "game_world.h"
#include "game_config.h"
#include "game_brain.h"

// 向游戏状态中添加一个新的实体
internal entity *BeginEntity(game_mode_world *WorldMode, world_position P) {
    Assert(WorldMode->CreationBufferIndex < ArrayCount(WorldMode->CreationBuffer));

    entity *EntityLow = WorldMode->CreationBuffer + WorldMode->CreationBufferIndex++;
    ZeroStruct(*EntityLow);
    EntityLow->XAxis = v2{1, 0};
    EntityLow->YAxis = v2{0, 1};

    EntityLow->ID.Value = ++WorldMode->LastUsedEntityStorageIndex;

    EntityLow->Collision = WorldMode->NullCollision;
    EntityLow->ChunkP = P;
    return EntityLow;
}

inline bool32 HasRoomFor(world_entity_block *Block, uint32 Size) {
    bool32 Result = (Block->EntityDataSize + Size) <= sizeof(Block->EntityData);
    return Result;
}

internal void PackEntityIntoChunk(world *World, sim_region *SimRegion, entity *Source,
                                  world_chunk *Chunk) {
    uint32 PackSize = sizeof(*Source);
    world_entity_block *Block = Chunk->FirstBlock;
    if (!Block || !HasRoomFor(Block, PackSize)) {
        if (!World->FirstFreeBlock) {
            World->FirstFreeBlock = PushStruct(&World->Arena, world_entity_block);
            World->FirstFreeBlock->Next = 0;
        }
        world_entity_block *NewBlock = World->FirstFreeBlock;
        World->FirstFreeBlock = NewBlock->Next;

        ClearWorldEntityBlock(NewBlock);
        NewBlock->Next = Chunk->FirstBlock;
        Chunk->FirstBlock = NewBlock;
    }
    world_entity_block *Block2 = Chunk->FirstBlock;
    Assert(HasRoomFor(Block2, PackSize));
    uint8 *Dest = (Block2->EntityData + Block2->EntityDataSize);
    Block2->EntityDataSize += PackSize;
    ++Block2->EntityCount;
    entity *DestE = (entity *)Dest;
    *DestE = *Source;
    PackTraversableReference(SimRegion, &DestE->Occupying);
    PackTraversableReference(SimRegion, &DestE->CameFrom);
    DestE->ddP = v3{0, 0, 0};
    DestE->ddtBob = 0.0f;
}

void PackEntityIntoWorld(world *World, sim_region *SimRegion, entity *Source, world_position At) {
    world_chunk *Chunk = GetWorldChunk(World, At.ChunkX, At.ChunkY, At.ChunkZ, &World->Arena);
    Source->P = At.Offset_;
    PackEntityIntoChunk(World, SimRegion, Source, Chunk);
}

internal void EndEntity(game_mode_world *WorldMode, entity *EntityLow, world_position P) {
    --WorldMode->CreationBufferIndex;
    Assert(EntityLow == (WorldMode->CreationBuffer + WorldMode->CreationBufferIndex));
    EntityLow->P = P.Offset_;
    PackEntityIntoWorld(WorldMode->World, 0, EntityLow, P);
}
void DeleteLowEntity(sim_region *Region, entity *Entity) {
    (void)Region;
    if (Entity) {
        Entity->Flags |= EntityFlag_Deleted;
    }
}

internal entity *BeginGroundEntity(game_mode_world *WorldMode, world_position P,
                                   entity_collision_volume_group *Collision) {
    entity *Entity = BeginEntity(WorldMode, P);
    Entity->Collision = Collision;
    return Entity;
}

// 将瓦片位置转换为区块空间的坐标
inline world_position ChunkPositionFromTilePosition(world *World, int32 AbsTileX, int32 AbsTileY,
                                                    int32 AbsTileZ,
                                                    v3 AddtionalOffset = {0.0f, 0.0f, 0.0f}) {
    // 初始化基础位置为零或默认值
    world_position BasePos = {};  // 作为基础位置，默认为零或未设置

    real32 TileSideInMeters = 1.4f;
    real32 TileDepthInMeters = World->ChunkDimInMeters.z;

    // 定义一个 v3 变量 TileDim，表示瓦片的尺寸，包含瓦片的侧边长度和深度。
    // World->TileSideInMeters 表示瓦片的边长（单位：米），World->TileDepthInMeters
    // 表示瓦片的深度（单位：米）。
    v3 TileDim = v3{TileSideInMeters, TileSideInMeters, TileDepthInMeters};

    // 计算偏移量 Offset，使用 Hadamard 乘积（按元素相乘）将瓦片尺寸与瓦片的绝对坐标相乘，
    // 这样得到该瓦片在世界中的位置。
    // AbsTileX, AbsTileY 和 AbsTileZ 分别表示瓦片的绝对位置坐标（在 X, Y, Z 轴上的坐标），
    // V3((real32)AbsTileX, (real32)AbsTileY, (real32)AbsTileZ) 将这些坐标转换为 real32 类型的向量。
    v3 Offset = Hadamard(TileDim, V3((real32)AbsTileX, (real32)AbsTileY, (real32)AbsTileZ));

    Offset.z -= 0.4f * TileDepthInMeters;
    // 将计算得到的偏移量映射到区块空间
    // 这个函数将偏移量调整到区块空间内，返回映射后的结果
    world_position Result =
        MapIntoChunkSpace(World, BasePos, Offset + AddtionalOffset);  // 将偏移量映射到区块空间

    // 确保结果的位置是规范的（符合系统规则）
    // 这个断言检查计算得到的偏移量是否符合预期的区块空间规范
    Assert(IsCanonical(World, Result.Offset_));  // 确保结果的偏移量是有效的，符合预期规则

    // 返回映射后的区块位置
    return Result;  // 返回计算得到的区块位置
}

internal void AddFloatyThing(game_mode_world *WorldMode, uint32 AbsTileX, uint32 AbsTileY,
                             uint32 AbsTileZ) {
    world_position P =
        ChunkPositionFromTilePosition(WorldMode->World, AbsTileX, AbsTileY, AbsTileZ);
    entity *Entity = BeginGroundEntity(WorldMode, P, WorldMode->FloorCollision);
    EndEntity(WorldMode, Entity, P);
}

struct standard_room {
    world_position P[17][9];
    traversable_reference Ground[17][9];
};

// 向游戏状态中添加一个标准房间实体
internal standard_room AddStandardRoom(game_mode_world *WorldMode, uint32 AbsTileX, uint32 AbsTileY,
                                       uint32 AbsTileZ, random_series *Series) {
    (void)Series;
    standard_room Result = {};
    for (int32 OffsetY = -4; OffsetY <= 4; ++OffsetY) {
        for (int32 OffsetX = -8; OffsetX <= 8; ++OffsetX) {
            world_position P = ChunkPositionFromTilePosition(WorldMode->World, AbsTileX + OffsetX,
                                                             AbsTileY + OffsetY, AbsTileZ);
            traversable_reference StandingOn = {};

#if 0
            P.Offset_.x += 0.25f * RandomBilateral(Series);
            P.Offset_.y += 0.25f * RandomBilateral(Series);
#endif
            if ((OffsetX >= -5) && (OffsetX <= -3) && (OffsetY >= 0) && (OffsetY <= 1)) {
            } else {
                if ((OffsetX == 3) && (OffsetY >= -2) && (OffsetY <= 2)) {
                    P.Offset_.z += 0.5f * (real32)(OffsetY + 4);
                }
                // P.Offset_.z = 0.25f * (real32)(OffsetX + OffsetY);
                if ((OffsetX == 2) && (OffsetY == 2)) {
                    entity *Entity = BeginGroundEntity(WorldMode, P, WorldMode->FloorCollision);
                    StandingOn.Entity.Index = Entity->ID;
                    Entity->TraversableCount = 1;
                    Entity->Traversables[0].P = v3{0, 0, 0};
                    Entity->Traversables[0].Occupier = 0;
                    EndEntity(WorldMode, Entity, P);
                } else {
                    entity *Entity = BeginGroundEntity(WorldMode, P, WorldMode->FloorCollision);
                    StandingOn.Entity.Index = Entity->ID;
                    Entity->TraversableCount = 1;
                    Entity->Traversables[0].P = v3{0, 0, 0};
                    Entity->Traversables[0].Occupier = 0;
                    EndEntity(WorldMode, Entity, P);
                }
            }
            Result.P[OffsetX + 8][OffsetY + 4] = P;
            Result.Ground[OffsetX + 8][OffsetY + 4] = StandingOn;
        }
    }
    return Result;
}
internal void AddPiece(entity *Entity, assets_type_id AssetType, real32 Height, v3 Offset, v4 Color,
                       uint32 Flags = 0) {
    Assert(Entity->PieceCount < ArrayCount(Entity->Pieces));
    entity_visible_piece *Piece = Entity->Pieces + Entity->PieceCount++;
    Piece->AssetType = AssetType;
    Piece->Height = Height;
    Piece->Color = Color;
    Piece->Offset = Offset;
    Piece->Flags = Flags;
}
// 定义添加墙壁的函数
internal void AddWall(game_mode_world *WorldMode, world_position P,
                      traversable_reference StandingOn) {
    // 从游戏状态中添加一个新的实体，并返回实体的索引
    entity *Entity = BeginGroundEntity(WorldMode, P, WorldMode->WallCollision);

    // 为指定的实体添加“碰撞”标志。
    // 通过调用 AddFlags 函数，将 EntityFlag_Collides 标志添加到 Entity 的 Flags
    // 字段中，表示该实体可以发生碰撞。
    AddFlags(Entity, EntityFlag_Collides);
    AddPiece(Entity, Asset_Tree, 2.5f, v3{0, 0, 0},
             v4{1, RandomUnilateral(&WorldMode->EffectsEntropy), 1, 1});
    Entity->Occupying = StandingOn;
    EndEntity(WorldMode, Entity, P);
}

internal void AddStair(game_mode_world *WorldMode, int32 AbsTileX, int32 AbsTileY, int32 AbsTileZ) {
    world_position P =
        ChunkPositionFromTilePosition(WorldMode->World, AbsTileX, AbsTileY, AbsTileZ);

    // 从游戏状态中添加一个新的实体，并返回实体的索引
    entity *Entity = BeginGroundEntity(WorldMode, P, WorldMode->StairCollision);

    AddFlags(Entity, EntityFlag_Collides);

    Entity->WalkableDim = Entity->Collision->TotalVolume.Dim.xy;
    // 设置实体的可行走高度，将其设为游戏状态中世界的瓦片深度（以米为单位）。
    // 这确保该实体在模拟中能够根据其所在区域的高度正确计算其可行走的区域。
    Entity->WalkableHeight = WorldMode->TypicalFloorHeight;

    EndEntity(WorldMode, Entity, P);
}

internal void InitHitPoints(entity *EntityLow, uint32 HitPointCount) {
    // 确保提供的命中点数不超过实体可以持有的最大数量
    Assert(HitPointCount < ArrayCount(EntityLow->HitPoint));

    // 设置实体的最大命中点数
    EntityLow->HitPointMax = HitPointCount;
    // 将 'EntityLow' 实体的最大命中点数设置为 'HitPointCount'

    // 遍历每个命中点并进行初始化
    for (uint32 HitPointIndex = 0; HitPointIndex < EntityLow->HitPointMax; ++HitPointIndex) {
        // 获取当前命中点
        hit_point *HitPoint = EntityLow->HitPoint + HitPointIndex;

        // 初始化命中点的标志字段为 0
        HitPoint->Flags = 0;

        // 设置命中点的填充量为初始值，通常为未填充的状态
        HitPoint->FilledAmount = HIT_POINT_SUB_COUNT;
        // 初始填充量设为 'HIT_POINT_SUB_COUNT' 常量值
    }
}
#define gShadowAlpha 0.5f
// 内部函数：用于在指定位置添加一个怪物并返回该实体的结果
void AddMonster(game_mode_world *WorldMode, world_position P, traversable_reference StandingOn) {
    // 调用 AddLowEntity 函数在指定位置添加一个低频实体（怪物），返回添加的实体结果
    entity *Entity = BeginGroundEntity(WorldMode, P, WorldMode->MonstarCollision);

    Entity->BrainSlot = BrainSlotFor(brain_monster, Body);
    Entity->BrainID = AddBrain(WorldMode);
    Entity->Occupying = StandingOn;
    // 为指定的实体添加“碰撞”标志。
    // 通过调用 AddFlags 函数，将 EntityFlag_Collides 标志添加到 Entity 的 Flags
    // 字段中，表示该实体可以发生碰撞。
    AddFlags(Entity, EntityFlag_Collides);

    InitHitPoints(Entity, 3);
    // 绘制角色的阴影图像
    AddPiece(Entity, Asset_Shadow, 4.5f, v3{0, 0, 0}, v4{1.0f, 1.0f, 1.0f, gShadowAlpha});
    // 绘制角色的躯干图像，依据角色朝向（FacingDirection），并在指定的屏幕坐标位置
    AddPiece(Entity, Asset_Torso, 4.5f, v3{0, 0, 0}, v4{1, 1, 1, 1});

    EndEntity(WorldMode, Entity, P);
}

void AddSnakeSegment(game_mode_world *WorldMode, world_position P, traversable_reference StandingOn,
                     brain_id BrainID, uint16 SegmentIndex) {
    // 调用 AddLowEntity 函数在指定位置添加一个低频实体（SnakePiece），返回添加的实体结果
    entity *Entity = BeginGroundEntity(WorldMode, P, WorldMode->MonstarCollision);

    Entity->BrainSlot = IndexedBrainSlotFor(brain_snake, Segments, SegmentIndex);
    Entity->BrainID = BrainID;
    Entity->Occupying = StandingOn;
    // 为指定的实体添加“碰撞”标志。
    // 通过调用 AddFlags 函数，将 EntityFlag_Collides 标志添加到 Entity 的 Flags
    // 字段中，表示该实体可以发生碰撞。
    AddFlags(Entity, EntityFlag_Collides);

    InitHitPoints(Entity, 3);
    // 绘制角色的阴影图像
    AddPiece(Entity, Asset_Shadow, 1.5f, v3{0, 0, 0}, v4{1.0f, 1.0f, 1.0f, gShadowAlpha});
    AddPiece(Entity, SegmentIndex ? Asset_Torso : Asset_Head, 1.5f, v3{0, 0, 0}, v4{1, 1, 1, 1});

    EndEntity(WorldMode, Entity, P);
}

internal void AddFamiliar(game_mode_world *WorldMode, world_position P,
                          traversable_reference StandingOn) {
    // 调用 AddLowEntity 函数，将一个新的熟悉的实体（EntityType_Familiar）添加到游戏状态中
    entity *Entity = BeginGroundEntity(WorldMode, P, WorldMode->FamiliarCollision);

    Entity->BrainSlot = BrainSlotFor(brain_familiar, Head);
    Entity->BrainID = AddBrain(WorldMode);
    Entity->Occupying = StandingOn;

    AddPiece(Entity, Asset_Shadow, 2.5f, v3{0, 0, 0}, v4{1, 1, 1, gShadowAlpha});
    // 绘制角色的头部图像，依据角色朝向（FacingDirection），并在指定的屏幕坐标位置
    AddPiece(Entity, Asset_Head, 2.5f, v3{0, 0, 0}, v4{1, 1, 1, 1});

    // 为指定的实体添加“碰撞”标志。
    // 通过调用 AddFlags 函数，将 EntityFlag_Collides 标志添加到 Entity 的 Flags
    // 字段中，表示该实体可以发生碰撞。
    AddFlags(Entity, EntityFlag_Collides);
    EndEntity(WorldMode, Entity, P);
}

// 提前声明一下
internal void AddCollisionRule(game_mode_world *WorldMode, uint32 IDA, uint32 IDB,
                               bool32 ShouldCollision);
brain_id AddBrain(game_mode_world *WorldMode) {
    brain_id ID = {++WorldMode->LastUsedEntityStorageIndex};
    return ID;
}

internal void AddPlayer(game_mode_world *WorldMode, sim_region *SimRegion,
                        traversable_reference Occupying, brain_id BrainID) {
    Occupying.Entity.Ptr->P = {5, -2, 1};
    world_position P =
        MapIntoChunkSpace(SimRegion->World, SimRegion->Origin, GetSimSpaceTraversable(Occupying).P);

    // 添加一个新的低频实体，返回Entity
    entity *Head = BeginGroundEntity(WorldMode, P, WorldMode->HeroHeadCollision);

    // 为指定的实体添加“碰撞”标志。
    // 通过调用 AddFlags 函数，将 EntityFlag_Collides 标志添加到 Entity 的 Flags
    // 字段中，表示该实体可以发生碰撞。
    AddFlags(Head, EntityFlag_Collides);

    entity *Body = BeginGroundEntity(WorldMode, P, WorldMode->HeroBodyCollision);

    AddFlags(Body, EntityFlag_Collides);

    entity *Glove = BeginGroundEntity(WorldMode, P, WorldMode->HeroGloveCollision);
    AddFlags(Glove, EntityFlag_Collides);
    Glove->MovementMode = MovementMode_AngleOffset;
    Glove->AngleCurrent = -0.25f * Tau32;
    Glove->AngleBaseDistance = 0.3f;
    Glove->AngleSwipDistance = 1.0f;
    Glove->AngleCurrentDistance = 0.3f;

    // 调用 InitHitPoints 函数，初始化 'Entity.Low' 实体的命中点
    // 传入的参数是 'Entity.Low' 实体以及命中点数量 3，表示该实体将拥有 3 个命中点
    InitHitPoints(Body, 3);
    Body->Occupying = Occupying;
    Body->BrainSlot = BrainSlotFor(brain_hero, Body);
    Body->BrainID = BrainID;
    Head->BrainSlot = BrainSlotFor(brain_hero, Head);
    Head->BrainID = BrainID;
    Glove->BrainSlot = BrainSlotFor(brain_hero, Glove);
    Glove->BrainID = BrainID;
    // 如果当前的摄像机正在跟随的实体为空，则将新添加的实体设置为摄像机的跟随实体
    if (WorldMode->CameraFollowingEntityIndex.Value == 0) {
        WorldMode->CameraFollowingEntityIndex = Head->ID;
    }
    // 绘制角色的阴影图像
    real32 HeroSizeC = 2.5f;

    v4 Color = {1, 1, 1, 1};
    AddPiece(Body, Asset_Shadow, HeroSizeC * 1.0f, v3{0, 0, 0}, v4{1, 1, 1, gShadowAlpha});
    AddPiece(Body, Asset_Torso, HeroSizeC * 1.2f, v3{0, 0.0f, 0}, Color, PieceMove_AxesDeform);
    AddPiece(Body, Asset_Cape, HeroSizeC * 1.2f, v3{0, -0.1f, 0}, Color,
             PieceMove_AxesDeform | PieceMove_BobOffset);
    // 绘制角色的头部图像，依据角色朝向（FacingDirection），并在指定的屏幕坐标位置
    AddPiece(Head, Asset_Head, HeroSizeC * 1.2f, v3{0, -0.25f, 0}, v4{1, 1, 1, 1});
    AddPiece(Glove, Asset_Rock, HeroSizeC * 0.2f, v3{0, 0, 0}, Color);

    EndEntity(WorldMode, Glove, P);
    EndEntity(WorldMode, Body, P);
    EndEntity(WorldMode, Head, P);
}
brain_hash *GetHashFromID(sim_region *SimRegion, brain_id BrainID);
brain *GetOrAddBrain(sim_region *SimRegion, brain_id ID, brain_type Type) {
    brain *Result = 0;
    brain_hash *Hash = GetHashFromID(SimRegion, ID);
    Result = Hash->Ptr;
    if (!Result) {
        Assert(SimRegion->BrainCount < SimRegion->MaxBrainCount);
        Result = SimRegion->Brains + SimRegion->BrainCount++;
        Result->ID = ID;
        Result->Type = Type;
        Hash->Ptr = Result;
    }
    return Result;
}

// 清除与指定存储索引相关的碰撞规则
internal void ClearCollisionRulesFor(game_mode_world *WorldMode, uint32 StorageIndex) {
    // 遍历碰撞规则哈希表中的所有桶
    // 注意：一种简化删除操作的方法是始终将存储索引对的两种顺序都添加到哈希表中，这样无论实体处于哪个位置，都可以找到它。
    // 然后，当你进行第一次删除操作时，只需记住空闲列表的原始顶部，完成后，再遍历空闲列表中的所有新项，并删除这些对的反向项。
    for (uint32 HashBucket = 0; HashBucket < ArrayCount(WorldMode->CollisionRuleHash);
         ++HashBucket) {
        // 遍历当前哈希桶中的链表
        for (pairwise_collision_rule **Rule =
                 &WorldMode->CollisionRuleHash[HashBucket];  // 指向当前链表的头部
             *Rule;) {                                       // 只要链表未结束，继续循环

            // 如果当前规则的两个存储索引都匹配目标索引，则认为找到了要移除的规则
            if (((*Rule)->IDA == StorageIndex) || ((*Rule)->IDB == StorageIndex)) {
                // 找到匹配的规则，记录为 RemoveRule
                pairwise_collision_rule *RemoveRule = *Rule;

                // 将 Rule 指向链表中的下一个规则
                *Rule = (*Rule)->NextInHash;

                // 将找到的规则从当前链表中移除，并将其放入空闲规则链表
                RemoveRule->NextInHash = WorldMode->FirstFreeCollisionRule;
                WorldMode->FirstFreeCollisionRule = RemoveRule;
            } else {
                // 如果当前规则不匹配，继续遍历链表
                Rule = &(*Rule)->NextInHash;
            }
        }
    }
}

internal bool32 RemoveCollisionRule(game_mode_world *WorldMode, uint32 IDA, uint32 IDB) {
    (void)WorldMode;
    (void)IDA;
    (void)IDB;
    return false;
}

// 向游戏状态中添加一条碰撞规则
internal void AddCollisionRule(game_mode_world *WorldMode, uint32 IDA, uint32 IDB,
                               bool32 CanCollision) {
    if (IDA > IDB) {
        uint32 Temp = IDA;  // 将 A 暂存到临时变量
        IDA = IDB;          // 将 B 赋值给 A
        IDB = Temp;         // 将原来的 A 赋值给 B
    }

    // 计算哈希桶索引
    // 使用 A 的存储索引，通过按位与操作确保索引在数组范围内
    uint32 HashBucket = IDA & (ArrayCount(WorldMode->CollisionRuleHash) - 1);

    // 初始化为未找到匹配规则
    pairwise_collision_rule *Found = 0;

    // 遍历指定哈希桶的链表，查找是否已经存在相同的规则
    for (pairwise_collision_rule *Rule = WorldMode->CollisionRuleHash[HashBucket];  // 遍历链表
         Rule;                                                                      //
         Rule = Rule->NextInHash) {                                                 //
        // 如果找到存储索引完全匹配的规则，记录为 Found
        if ((Rule->IDA == IDA) && (Rule->IDB == IDB)) {
            Found = Rule;
            break;  // 跳出循环，结束搜索
        }
    }

    // 如果没有找到匹配的规则，需要新建一条规则
    if (!Found) {
        Found = WorldMode->FirstFreeCollisionRule;
        // 如果内存分配成功，则将 `FirstFreeCollisionRule` 指向下一个空闲规则
        if (Found) {
            WorldMode->FirstFreeCollisionRule = Found->NextInHash;
        } else {
            // 如果内存分配失败，则再次尝试从内存池分配
            // 这里可能是为了做额外的内存检查，避免程序崩溃
            Found = PushStruct(&WorldMode->World->Arena, pairwise_collision_rule);
        }

        // 将新规则的 NextInHash 指向当前哈希桶的头部（链表的第一项）
        Found->NextInHash = WorldMode->CollisionRuleHash[HashBucket];

        // 将新规则插入哈希桶，将其设置为链表的新头部
        WorldMode->CollisionRuleHash[HashBucket] = Found;
    }

    // 无论是新创建还是找到的规则，更新规则的内容
    if (Found) {
        Found->IDA = IDA;                  // 设置规则中的存储索引 A
        Found->IDB = IDB;                  // 设置规则中的存储索引 B
        Found->CanCollide = CanCollision;  // 设置是否应发生碰撞的标志
    }
}

internal bool32 CanCollide(game_mode_world *WorldMode, entity *A, entity *B) {
    bool32 Result = false;

    if (A != B) {
        if (A->ID.Value > B->ID.Value) {
            entity *Temp = A;  // 临时存储 A
            A = B;             // 将 B 赋给 A
            B = Temp;          // 将临时存储的 A 赋给 B
        }

        if (IsSet(A, EntityFlag_Collides) && IsSet(B, EntityFlag_Collides)) {
            Result = true;

            uint32 HashBucket = A->ID.Value & (ArrayCount(WorldMode->CollisionRuleHash) - 1);
            for (pairwise_collision_rule *Rule = WorldMode->CollisionRuleHash[HashBucket];  //
                 Rule;                                                                      //
                 Rule = Rule->NextInHash) {
                if ((Rule->IDA == A->ID.Value) && (Rule->IDB == B->ID.Value)) {
                    Result = Rule->CanCollide;
                    break;
                }
            }
        }
    }
    return Result;
}
// 检查是否可以与给定区域重叠
bool32 CanOverlap(game_mode_world *WorldMode, entity *Mover, entity *Region) {
    (void)Mover;
    (void)WorldMode;
    bool32 Result = false;  // 默认重叠结果为 false
    if (Mover != Region) {
#if 0
        // 检查区域类型是否为楼梯井（EntityType_Stairwell）
        if (Region->Type == EntityType_Stairwell) {
            Result = true;  // 如果是楼梯井，则允许重叠
        }
#endif
    }

    return Result;  // 目前总是返回 false，需要修改逻辑来返回 Result
}

internal bool32 SpeculativeCollide(entity *Mover, entity *Region, v3 TestP) {
    TIMED_FUNCTION();
    bool32 Result = true;
    (void)Mover;
    (void)Region;
    (void)TestP;
#if 0
    // 如果重叠的区域是楼梯井（EntityType_Stairwell）
    if (Region->Type == EntityType_Stairwell) {
        // 通过插值将Y值与楼梯区域的最小和最大Z值进行映射，得到精确的地面高度
        real32 StepHeight = 0.1f;
#if 0
        // 计算结果，判断是否满足跳跃或跨越的条件
        Result = (AbsoluteValue(GetEntityGroundPoint(Mover).z - Ground) >
                  StepHeight) ||  // 判断当前Z轴位置与地面高度的差值是否大于步高（StepHeight）
                 ((Bary.y > 0.1f) && (Bary.y < 0.9f));
#endif
        // 获取当前实体（Mover）在地面上的位置
        v3 MoverGroundPoint = GetEntityGroundPoint(Mover, TestP);

        // 计算楼梯的地面高度，使用实体的地面位置作为参考点
        real32 Ground = GetStairGround(Region, MoverGroundPoint);

        // 检查实体与楼梯地面高度的差值是否大于步高（StepHeight）
        // 如果差值大于步高，表示实体需要跨越的高度超过了允许的步伐高度
        Result = (AbsoluteValue(MoverGroundPoint.z - Ground) > StepHeight);
    }
#endif
    return Result;
}

internal bool32 EntitiesOverlap(entity *Entity, entity *TestEntity, v3 Epsilon = v3{0, 0, 0}) {
    TIMED_FUNCTION();
    bool32 Result = false;
    for (uint32 EntityVolumeIndex = 0;
         !Result && EntityVolumeIndex < Entity->Collision->VolumeCount; ++EntityVolumeIndex) {
        entity_collision_volume *Volume = Entity->Collision->Volumes + EntityVolumeIndex;
        for (uint32 TestVolumeIndex = 0;
             !Result && TestVolumeIndex < TestEntity->Collision->VolumeCount; ++TestVolumeIndex) {
            entity_collision_volume *TestVolume = TestEntity->Collision->Volumes + TestVolumeIndex;

            // 获取当前实体的矩形边界，假设矩形是通过中心点和尺寸计算得出的
            rectangle3 EntityRect =
                RectCenterDim(Entity->P + Volume->OffsetP, Volume->Dim + Epsilon);

            // 获取测试实体的矩形边界
            rectangle3 TestEntityRect =
                RectCenterDim(TestEntity->P + TestVolume->OffsetP, TestVolume->Dim);

            // 检查两个矩形是否相交
            Result = RectanglesIntersect(EntityRect, TestEntityRect);
        }
    }
    return Result;
}

// 定义内联函数 HandleCollision，用于处理两个实体 A 和 B 之间的碰撞
inline bool32 HandleCollision(game_mode_world *WorldMode, entity *A,  // 第一个实体 A
                              entity *B                               // 第二个实体 B
) {
    (void)A;
    (void)B;
    (void)WorldMode;
    // 检查实体是否设置了碰撞标志，若设置了，则表示实体在碰撞时停止
    bool32 StopsOnCollistion = false;
#if 0
    // 如果 A 的类型大于 B 的类型，则交换 A 和 B 的指向
    // 这样确保 A 始终指向类型较小的实体，B 指向类型较大的实体
    if (A->Type > B->Type) {
        entity *Temp = A;  // 临时存储 A
        A = B;             // 将 B 赋给 A
        B = Temp;          // 将临时存储的 A 赋给 B
    }
#endif
    return StopsOnCollistion;
}

struct test_wall {
    real32 X;
    real32 RelX;
    real32 RelY;
    real32 DeltaX;
    real32 DeltaY;
    real32 MinY;
    real32 MaxY;
    v3 Normal;
};

void MoveEntity(game_mode_world *WorldMode, sim_region *SimRegion, entity *Entity, real32 dt,
                v3 ddP) {
    TIMED_FUNCTION();

    world *World = SimRegion->World;
    (void)World;

    // 计算玩家位置的更新增量（PlayerDelta）
    // 位置更新由加速度（ddP）和时间步长（dt）的平方关系计算，以及速度（dP）与时间步长的线性关系
    v3 PlayerDelta = 0.5f * ddP * Square(dt) +  // 加速度部分（位移）
                     Entity->dP * dt;           // 速度部分（线性位移）

    // 更新玩家的速度，包括加速度和之前的速度
    Entity->dP = ddP * dt + Entity->dP;
    // 断言实体的速度平方（dP的长度平方）不超过最大实体速度的平方，确保实体的速度在允许的范围内
    Assert(LengthSq(Entity->dP) <= Square(SimRegion->MaxEntityVelocity));

    real32 DistanceRemaining = Entity->DistanceLimit;
    if (DistanceRemaining == 0.0f) {
        // TODO：我们是否想要将这个规范一下
        DistanceRemaining = 10000.0f;
    }
    // 剩余的时间步，初始化为 1.0f，表示一次完整的运动
    // 循环最多执行 4 次或直到剩余时间为 0
    for (uint32 Iteration = 0; Iteration < 4; Iteration++) {
        real32 tMin = 1.0f;
        real32 tMax = 1.0f;
        real32 PlayerDeltaLength = Length(PlayerDelta);
        // TODO: 我们在这里应该如何处理epsilon？考虑一下最终碰撞代码的处理方式。
        if (PlayerDeltaLength > 0.0f) {
            if (PlayerDeltaLength > DistanceRemaining) {
                tMin = DistanceRemaining / PlayerDeltaLength;
            }
            // 墙的法向量，初始化为空向量
            v3 WallNormalMin = {};
            v3 WallNormalMax = {};
            entity *HitEntityMin = 0;
            entity *HitEntityMax = 0;

            // 如果标志位包含 EntityFlag_Collides，执行后续碰撞检测相关代码。
            // 遍历所有的实体碰撞检测
            for (uint32 TestHighEntityIndex = 1; TestHighEntityIndex < SimRegion->EntityCount;
                 ++TestHighEntityIndex) {
                entity *TestEntity = SimRegion->Entities + TestHighEntityIndex;

                // 定义一个很小的浮点值，用于处理浮点精度问题，避免计算误差导致逻辑错误。
                real32 tEpsilon = 0.01f;

                // 判断是不是应该发生碰撞
                if (CanCollide(WorldMode, Entity, TestEntity)) {
                    for (uint32 EntityVolumeIndex = 0;
                         EntityVolumeIndex < Entity->Collision->VolumeCount; ++EntityVolumeIndex) {
                        entity_collision_volume *Volume =
                            Entity->Collision->Volumes + EntityVolumeIndex;
                        for (uint32 TestVolumeIndex = 0;
                             TestVolumeIndex < TestEntity->Collision->VolumeCount;
                             ++TestVolumeIndex) {
                            entity_collision_volume *TestVolume =
                                TestEntity->Collision->Volumes + TestVolumeIndex;

                            // 计算两个实体在宽度方向上的总直径
                            v3 MinkowskiDiameter = {TestVolume->Dim.x + Volume->Dim.x,
                                                    TestVolume->Dim.y + Volume->Dim.y,
                                                    TestVolume->Dim.z + Volume->Dim.z};

                            // 计算碰撞区域的最小角点（左下角）和最大角点（右上角）
                            v3 MinCorner = -0.5f * MinkowskiDiameter;  // 碰撞区域的最小角点
                            v3 MaxCorner = 0.5f * MinkowskiDiameter;  // 碰撞区域的最大角点

                            v3 Rel = ((Entity->P + Volume->OffsetP) -
                                      (TestEntity->P + TestVolume->OffsetP));
                            // 计算两个碰撞体积之间的相对位置。
                            // Entity->P 是当前实体的位置。
                            // Volume->OffsetP 是当前碰撞体积的偏移量。
                            // TestEntity->P 是测试实体的位置。
                            // TestVolume->OffsetP
                            // 是测试碰撞体积的偏移量。
                            // 通过将两者相减，得到两个碰撞体积在3D空间中的相对位置向量。

                            // TODO: 我们是否需要在最大角落使用开区间（不包括最大值）
                            if ((Rel.z >= MinCorner.z) && (Rel.z < MaxCorner.z)) {
                                test_wall Walls[] = {
                                    // 测试角色与墙体最小 X 边界的碰撞
                                    {MinCorner.x, Rel.x, Rel.y, PlayerDelta.x, PlayerDelta.y,
                                     MinCorner.y, MaxCorner.y, v3{-1, 0, 0}},
                                    // 测试角色与墙体最大 X 边界的碰撞
                                    {MaxCorner.x, Rel.x, Rel.y, PlayerDelta.x, PlayerDelta.y,
                                     MinCorner.y, MaxCorner.y, v3{1, 0, 0}},
                                    // 测试角色与墙体最小 Y 边界的碰撞
                                    {MinCorner.y, Rel.y, Rel.x, PlayerDelta.y, PlayerDelta.x,
                                     MinCorner.x, MaxCorner.x, v3{0, -1, 0}},
                                    // 测试角色与墙体最大 Y 边界的碰撞
                                    {MaxCorner.y, Rel.y, Rel.x, PlayerDelta.y, PlayerDelta.x,
                                     MinCorner.x, MaxCorner.x, v3{0, 1, 0}}};
                                real32 tMinTest = tMin;
                                bool32 HitThis = false;
                                v3 TestWallNormal = {};

                                for (uint32 WallIndex = 0; WallIndex < ArrayCount(Walls);
                                     ++WallIndex) {
                                    test_wall *Wall = Walls + WallIndex;

                                    if (Wall->DeltaX != 0.0f) {
                                        // 计算当前瓦片的 X 方向上的碰撞时间 tResult
                                        // 玩家从当前相对位置 RelX 移动到右边墙 WallX
                                        // 的碰撞时间
                                        real32 tResult = (Wall->X - Wall->RelX) / Wall->DeltaX;

                                        // 计算在 tResult 时刻玩家在 Y 方向上的位置
                                        // 使用线性插值公式，tResult
                                        // 表示到达该墙的时间，DeltaY 表示玩家在 Y
                                        // 轴上的速度
                                        real32 Y = Wall->RelY + tResult * Wall->DeltaY;

                                        // 检查 tResult 是否大于等于 0，且 tMin 是否大于
                                        // tResult。 如果 tResult 小于
                                        // 0，表示碰撞发生在玩家起始点之前，不会考虑这个碰撞。
                                        // 如果 tMin 大于
                                        // tResult，表示当前碰撞距离比之前发现的碰撞距离更近，需更新最小碰撞时间。
                                        if ((tResult >= 0.0f) && (tMinTest > tResult)) {
                                            // 检查计算得到的 Y
                                            // 坐标是否在瓦片的垂直范围内（即瓦片的最小角和最大角之间）。
                                            // 只有当 Y
                                            // 值位于当前瓦片的有效范围内时，才能确认碰撞有效。
                                            if ((Y >= Wall->MinY) && (Y <= Wall->MaxY)) {
                                                // 更新 tMin 为当前
                                                // tResult，表示发现了一个更近的有效碰撞。
                                                // 由于浮点精度损失，角色走过顶部墙壁
                                                // 如果满足所有条件，更新最小碰撞时间
                                                // *tMin。 减去一个小的
                                                // tEpsilon，用于处理浮点精度问题，确保玩家不会贴到墙上。
                                                tMinTest =
                                                    Maximum(0.0f, tResult - tEpsilon);  // 取最大值
                                                // 碰撞发生在最小 X 边界，墙体法向量为 (1,
                                                // 0)
                                                TestWallNormal = Wall->Normal;
                                                HitThis = true;
                                            }
                                        }
                                    }
                                }
                                // 如果发生了碰撞
                                if (HitThis) {
                                    // 计算测试点的位置，测试点是在实体当前位置加上根据tMinTest和玩家的移动量（PlayerDelta）计算出的偏移
                                    v3 TestP = Entity->P + tMinTest * PlayerDelta;

                                    // 使用试探性碰撞检测检查是否与目标实体发生碰撞
                                    if (SpeculativeCollide(Entity, TestEntity, TestP)) {
                                        // 如果试探性碰撞成功，更新最小碰撞时间
                                        tMin = tMinTest;

                                        // 更新墙壁的法线方向
                                        WallNormalMin = TestWallNormal;

                                        // 更新被碰撞的实体
                                        HitEntityMin = TestEntity;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            v3 WallNormal;
            entity *HitEntity;
            real32 tStop;
            if (tMin < tMax) {
                tStop = tMin;
                HitEntity = HitEntityMin;
                WallNormal = WallNormalMin;
            } else {
                tStop = tMax;
                HitEntity = HitEntityMax;
                WallNormal = WallNormalMax;
            }
            Entity->P += tStop * PlayerDelta;
            DistanceRemaining -= tStop * PlayerDeltaLength;
            if (HitEntity) {
                // 根据是否发生重叠以及碰撞是否停止，处理碰撞
                bool32 StopsOnCollistion = HandleCollision(WorldMode, Entity, HitEntity);

                if (StopsOnCollistion) {
                    // 计算玩家速度在反射方向上的反射分量，并更新玩家速度
                    // 反射公式：dP = dP - 1 * (dP · WallNormal) * WallNormal
                    // 参数2反弹1滑翔
                    // 通过计算速度与墙法向量的内积，得到与墙的反向速度分量并进行滑翔
                    Entity->dP = Entity->dP - 1 * Inner(Entity->dP, WallNormal) * WallNormal;

                    // 更新玩家的移动速度（在与墙碰撞的方向上做滑翔处理）
                    PlayerDelta = PlayerDelta - 1 * Inner(PlayerDelta, WallNormal) * WallNormal;
                }

            } else {
                break;
            }
        } else {
            break;
        }
    }

    if (Entity->DistanceLimit != 0.0f) {
        Entity->DistanceLimit = DistanceRemaining;
    }
#if 0
    // 判断角色速度向量的 x 分量是否大于 y 分量的绝对值
    if ((AbsoluteValue(Entity->dP.x) == 0.0f) && (AbsoluteValue(Entity->dP.y) == 0.0f)) {
    }
    // 判断角色速度向量的 y 分量是否大于 x 分量的绝对值
    else {
        Entity->FacingDirection = ATan2(Entity->dP.y, Entity->dP.x);
        if (Entity->FacingDirection < 0.0f) {
            Entity->FacingDirection += Tau32;
        }
    }
#endif
}

void DrawHitpoints(entity *Entity, render_group *RenderGroup, object_transform Transform) {
    // 检查 LowEntity 的 HighEntityIndex 是否大于等于 1
    if (Entity->HitPointMax >= 1) {
        // 定义每个生命点的尺寸，单位可能为屏幕像素
        v2 HealthDim = {0.2f, 0.2f};

        // 定义生命点之间的水平间距，具体数值待定
        real32 SpacingX = 1.5f * HealthDim.x;

        // 参考公式 0.5f * (n-1) * s
        real32 FirstX = 0.5f * (Entity->HitPointMax - 1) * SpacingX;
        v2 HitP = {-FirstX, -0.2f};
        v2 dHitP = {SpacingX, 0.0f};
        // 遍历 LowEntity
        // 中的所有生命值点（HitPoints），直到最大生命值（HitPointMax）为止
        for (uint32 HealthIndex = 0; HealthIndex < Entity->HitPointMax; HealthIndex++) {
            // 获取当前生命点 (HitPoint)，根据 HealthIndex 从 LowEntity 的 HitPoint
            // 数组中获取
            hit_point *HitPoint = Entity->HitPoint + HealthIndex;

            // 默认颜色设置为红色，表示未填充的生命值
            v4 Color = {1.0f, 0.0f, 0.0f, 0.0f};

            // 如果当前生命点的填充量为 0，表示该生命点没有填充，设置颜色为灰色
            if (HitPoint->FilledAmount == 0) {
                Color.r = 0.2f;
                Color.g = 0.2f;
                Color.b = 0.2f;
            }

            // 使用 PushRect 函数绘制当前生命点的矩形，显示在生命条上
            // 参数解释：传入矩形的起始位置（0.0f,
            // 0.0f）、矩形的偏移（0）、生命条的尺寸（HealthDim）， 以及生命条的颜色（R,
            // G, B）和透明度（1.0f）
            PushRect(RenderGroup, &Transform, {HitP.x, HitP.y, 0}, HealthDim, Color);
            HitP += dHitP;
        }
    }
}

entity_collision_volume_group *MakeSimpleGroundedCollision(game_mode_world *WorldMode, real32 DimX,
                                                           real32 DimY, real32 DimZ,
                                                           real32 OffsetZ = 0.0f) {
    entity_collision_volume_group *Group =
        PushStruct(&WorldMode->World->Arena, entity_collision_volume_group);
    Group->VolumeCount = 1;
    Group->Volumes =
        PushArray(&WorldMode->World->Arena, Group->VolumeCount, entity_collision_volume);
    Group->TotalVolume.OffsetP = v3{0, 0, 0.5f * DimZ + OffsetZ};
    Group->TotalVolume.Dim = v3{DimX, DimY, DimZ};
    Group->Volumes[0] = Group->TotalVolume;
    return Group;
}

entity_collision_volume_group *MakeSimpleFloorCollision(game_mode_world *WorldMode, real32 DimX,
                                                        real32 DimY, real32 DimZ) {
    entity_collision_volume_group *Group =
        PushStruct(&WorldMode->World->Arena, entity_collision_volume_group);
    Group->TotalVolume.OffsetP = v3{0, 0, 0};
    Group->TotalVolume.Dim = v3{DimX, DimY, DimZ};
#if 1
    Group->VolumeCount = 1;
    Group->Volumes =
        PushArray(&WorldMode->World->Arena, Group->VolumeCount, entity_collision_volume);
    Group->TotalVolume.OffsetP = V3(0, 0, 0.5f * DimZ);
    Group->TotalVolume.Dim = V3(DimX, DimY, DimZ);
    Group->Volumes[0] = Group->TotalVolume;
#endif
    return Group;
}

entity_collision_volume_group *MakeNullCollision(game_mode_world *WorldMode) {
    entity_collision_volume_group *Group =
        PushStruct(&WorldMode->World->Arena, entity_collision_volume_group);
    Group->VolumeCount = 0;
    Group->Volumes = 0;
    Group->TotalVolume.OffsetP = v3{0, 0, 0};
    Group->TotalVolume.Dim = v3{0, 0, 0};
    return Group;
}

void PlayWorld(game_state *GameState, transient_state *TranState) {
    SetGameMode(GameState, TranState, GameMode_World);

    game_mode_world *WorldMode = PushStruct(&GameState->ModeArena, game_mode_world);

    // 在 WorldArena 内存池中为 World 对象分配内存
    WorldMode->World = PushStruct(&GameState->ModeArena, world);
    WorldMode->EffectsEntropy = RandomSeed(123);
    WorldMode->GameEntropy = RandomSeed(1234);
#if GAME_INTERNAL
    WorldMode->TestFont = MakeNothingsTest(&GameState->ModeArena);
#endif  // GAME_INTERNAL

    // 定义每屏幕宽度（以瓦片数量表示）
    uint32 TilesPerWidth = 17;

    // 定义每屏幕高度（以瓦片数量表示）
    uint32 TilesPerHeight = 9;
    // TODO: 等待完全稀疏化的实现
    // 为游戏状态设置不同类型的碰撞体群组，使用特定的函数来初始化每个碰撞体。
    WorldMode->LastUsedEntityStorageIndex = ReservedBrainID_FirstFree;
    WorldMode->TypicalFloorHeight = 3.0f;
    // 地面缓冲区的宽度和高度
    real32 PixelsToMeters = 1.0f / 42.0f;

    v3 WorldChunkDimInMeters = {PixelsToMeters * (real32)GroundBufferWidth,   //
                                PixelsToMeters * (real32)GroundBufferHeight,  //
                                WorldMode->TypicalFloorHeight};

    // 定义一个 world 类型的结构体实例，用于存储整个地图信息
    WorldMode->World = CreateWorld(WorldChunkDimInMeters, &GameState->ModeArena);

    real32 TileSideInMeters = 1.4f;
    real32 TileDepthInMeters = WorldMode->TypicalFloorHeight;

    // 为空碰撞体设置值，用于表示没有碰撞信息的实体。
    WorldMode->NullCollision = MakeNullCollision(WorldMode);
    // 为剑的碰撞体设置值，使用简单的地面碰撞，指定宽度、长度和高度。
    WorldMode->SwordCollision = MakeSimpleGroundedCollision(WorldMode, 0.5f, 1.0f, 0.1f);

    // 为楼梯的碰撞体设置值，使用简单的地面碰撞，指定楼梯的宽度、高度和厚度。
    WorldMode->StairCollision =
        MakeSimpleGroundedCollision(WorldMode,         //
                                    TileSideInMeters,  // 宽度：游戏世界中每个瓦片的边长
                                    2.0f * TileSideInMeters,  // 高度：楼梯的高度为两个瓦片的边长
                                    1.1f * TileDepthInMeters);  // 厚度：楼梯的厚度为瓦片深度的1.1倍

    // 为玩家的碰撞体设置值，指定玩家的宽度、高度和深度。
    WorldMode->HeroHeadCollision = MakeSimpleGroundedCollision(WorldMode, 0.5f, 0.5f, 0.4f, 0.7f);
    WorldMode->HeroBodyCollision = MakeSimpleGroundedCollision(WorldMode, 0.5f, 0.5f, 0.6f);
    WorldMode->HeroGloveCollision = MakeNullCollision(WorldMode);

    // 为怪物的碰撞体设置值，指定怪物的宽度、高度和深度。
    WorldMode->MonstarCollision = MakeNullCollision(WorldMode);
    // MakeSimpleGroundedCollision(WorldMode, 0.5f, 0.5f, 0.5f);

    // 为伙伴的碰撞体设置值，指定伙伴的宽度、高度和深度。
    WorldMode->FamiliarCollision = MakeNullCollision(WorldMode);
    // MakeSimpleGroundedCollision(WorldMode, 0.5f, 0.5f, 0.5f);

    // 为墙壁的碰撞体设置值，使用简单的地面碰撞，指定墙壁的宽度、高度和深度。
    WorldMode->WallCollision =
        MakeSimpleGroundedCollision(WorldMode,         //
                                    TileSideInMeters,  // 宽度：游戏世界中每个瓦片的边长
                                    TileSideInMeters,  // 高度：墙壁的高度等于瓦片的边长
                                    TileDepthInMeters);  // 厚度：墙壁的深度等于瓦片的深度

    WorldMode->FloorCollision = MakeSimpleFloorCollision(  //
        WorldMode,                                         //
        TileSideInMeters,                                  //
        TileSideInMeters,                                  //
        TileDepthInMeters);

    // 初始化摄像头位置，将其设置为地图的中央位置
    // 假设地图的宽度为17个瓦片，高度为9个瓦片，
    // CameraP.ChunkX 和 CameraP.ChunkY 分别表示摄像头在水平和垂直方向上的绝对瓦片位置。
    // 这里将摄像头设置为地图的中间，计算方式是将瓦片数量除以2，
    // 这样摄像头将对准地图的中央位置。
    // WorldMode->CameraP.ChunkX = 17 / 2;  // 水平方向摄像头位置设置为地图宽度的一半
    // WorldMode->CameraP.ChunkY = 9 / 2;  // 垂直方向摄像头位置设置为地图高度的一半

    // 定义屏幕的基础坐标，基于每屏瓦片数目计算，取中间值以确定中心位置
    uint32 ScreenBaseX = 0;  // 屏幕基础X坐标，基于瓦片宽度均分
    uint32 ScreenBaseY = 0;  // 屏幕基础Y坐标，基于瓦片高度均分
    uint32 ScreenBaseZ = 0;  // 屏幕基础Z坐标，取世界的中间深度

    // 初始化屏幕坐标为基础坐标
    uint32 ScreenX = ScreenBaseX;   // 当前屏幕X坐标
    uint32 ScreenY = ScreenBaseY;   // 当前屏幕Y坐标
    uint32 AbsTileZ = ScreenBaseZ;  // 当前屏幕所在的绝对瓦片层（Z坐标）

    bool32 DoorLeft = false;
    bool32 DoorRight = false;
    bool32 DoorTop = false;
    bool32 DoorBottom = false;
    bool32 DoorUp = false;
    bool32 DoorDown = false;
    uint32 DoorDirection = 0;
    random_series Series = WorldMode->GameEntropy;

    for (uint32 ScreenIndex = 0; ScreenIndex < 4; ScreenIndex++) {
// 如果当前楼层有楼梯（向上或向下），生成一个随机数，范围是 [0, 1]
// 从随机数表中取值并更新索引，模 2 以限制随机数在 0 和 1 之间
// 如果当前楼层没有楼梯，生成一个随机数，范围是 [0, 2]
// 从随机数表中取值并更新索引，模 3 以限制随机数在 0、1 和 2 之间
#if 0
        DoorDirection = RandomChoice(&Series, (DoorUp || DoorDown) ? 2 : 4);
#else
        DoorDirection = 2;  // RandomChoice(&Series, 2);
#endif
        // DoorDirection = 3;
        bool32 CreatedZDoor = false;  // 标记是否生成了垂直楼梯的标志变量
        // 根据随机数的值决定门的方向或楼梯的生成
        if (DoorDirection == 3) {
            CreatedZDoor = true;
            DoorDown = true;
        } else if (DoorDirection == 2) {
            CreatedZDoor = true;  // 标记生成了楼梯（垂直方向的门）
            DoorUp = true;
        } else if (DoorDirection == 1) {
            // 如果随机数是 1，生成右侧的门
            DoorRight = true;
        } else {
            // 如果随机数是 0，生成顶部的门
            DoorTop = true;
        }
        // 调用 AddStandardRoom 函数，以在指定的位置添加标准房间实体
        standard_room Room = AddStandardRoom(
            WorldMode,  // 游戏状态，包含所有的游戏数据和信息
            ScreenX * TilesPerWidth + TilesPerWidth / 2,  // 根据屏幕坐标计算标准房间的X坐标
            ScreenY * TilesPerHeight + TilesPerHeight / 2,  // 根据屏幕坐标计算标准房间的Y坐标
            AbsTileZ, &Series);  // 指定标准房间的Z坐标，通常为楼层的高度，表示房间所在的层级
#if 1
        // 调用 AddMonster 函数，在计算出的相机附近位置添加一个怪物
        AddMonster(WorldMode, Room.P[3][6], Room.Ground[3][6]);
        AddFamiliar(WorldMode, Room.P[3][3], Room.Ground[3][3]);
        brain_id SnakeBrainID = AddBrain(WorldMode);
        for (uint16 SegmentIndex = 0; SegmentIndex < 16; ++SegmentIndex) {
            uint16 Y = 4 + SegmentIndex;
            AddSnakeSegment(WorldMode, Room.P[12][Y], Room.Ground[12][Y], SnakeBrainID,
                            SegmentIndex);
        }
#endif
        // 内层循环：遍历当前屏幕网格内所有瓦片的 Y 坐标
        for (uint32 TileY = 0; TileY < ArrayCount(Room.P[0]); TileY++) {
            // 最内层循环：遍历当前屏幕网格内所有瓦片的 X 坐标
            for (uint32 TileX = 0; TileX < ArrayCount(Room.P); TileX++) {
                // `0` 代表没有瓦片（空白区域），`1` 代表可行走区域（或地面），而
                // `2` 可能代表墙壁或不可通过的区域，`3` 向上的楼道 , `4`向下的楼道

                world_position P = Room.P[TileX][TileY];
                traversable_reference Ground = Room.Ground[TileX][TileY];
                uint32 ShouldBeDoor = false;
                // 判断是否绘制左边的墙
                if (TileX == 0) {
                    if (!DoorLeft) {
                        ShouldBeDoor = true;  // 没有门，绘制墙
                    } else if (TileY != (TilesPerHeight / 2)) {
                        ShouldBeDoor = true;
                        // 有门，但当前位置不是中间，仍需绘制墙
                    }
                }

                // 判断是否绘制右边的墙
                if (TileX == (TilesPerWidth - 1)) {
                    if (!DoorRight) {
                        ShouldBeDoor = true;
                        // 没有门，绘制墙
                    } else if (TileY != (TilesPerHeight / 2)) {
                        ShouldBeDoor = true;
                        // 有门，但当前位置不是中间，仍需绘制墙
                    }
                }

                // 判断是否绘制底边的墙
                if (TileY == 0) {
                    if (!DoorBottom) {
                        ShouldBeDoor = true;
                        // 没有门，绘制墙
                    } else if (TileX != (TilesPerWidth / 2)) {
                        ShouldBeDoor = true;
                        // 有门，但当前位置不是中间，仍需绘制墙
                    }
                }

                // 判断是否绘制顶边的墙
                if (TileY == (TilesPerHeight - 1)) {
                    if (!DoorTop) {
                        ShouldBeDoor = true;
                        // 没有门，绘制墙
                    } else if (TileX != (TilesPerWidth / 2)) {
                        ShouldBeDoor = true;
                        // 有门，但当前位置不是中间，仍需绘制墙
                    }
                }
                if (ShouldBeDoor) {
                    AddWall(WorldMode, P, Ground);
                } else if (CreatedZDoor) {
#if 0
                    if (((AbsTileZ % 2 == 0) && (TileX == 10) && (TileY == 5)) ||
                        (!(AbsTileZ % 2 == 0) && (TileX == 4) && (TileY == 5))) {
                        AddStair(WorldMode, AbsTileX, AbsTileY, DoorDown ? AbsTileZ - 1 : AbsTileZ);
                    }
#endif
                }
            }
        }

        // 将上一轮的右侧门设置为当前轮的左侧门
        DoorLeft = DoorRight;

        // 将上一轮的顶部门设置为当前轮的底部门
        DoorBottom = DoorTop;

        // 重置当前轮的右侧门和顶部门状态，表示本轮尚未生成这些门
        DoorRight = false;
        DoorTop = false;

        if (CreatedZDoor) {
            // 如果本轮生成了楼梯（CreatedZDoor 为 true），切换楼梯的方向
            // 例如，如果上一轮生成了向上的楼梯，本轮需要将其关闭，并生成向下的楼梯
            DoorUp = !DoorUp;
            DoorDown = !DoorDown;
        } else {
            // 如果本轮没有生成楼梯（CreatedZDoor 为 false），确保楼梯的状态均为关闭
            DoorUp = false;
            DoorDown = false;
        }
        if (DoorDirection == 3) {
            AbsTileZ -= 1;
        }
        // 如果随机选择是 2（代表生成楼梯），切换楼层
        else if (DoorDirection == 2) {
            AbsTileZ += 1;
        } else if (DoorDirection == 1) {
            ScreenX += 1;  // 水平移动
        } else {
            ScreenY += 1;  // 垂直移动
        }
    }
#if 0

    // 此代码段通过一个循环不断添加墙体实体，直到低频实体的数量
    // 接近低频实体数组的容量上限（留出16个空余位置）。
    //
    // 参数说明：
    // - WorldMode->LowEntityCount: 当前低频实体的数量。
    // - ArrayCount(WorldMode->LowEntities): 低频实体数组的最大容量。
    // - 93: 预留空间，用于其他低频实体的动态添加。
    //
    // 添加墙体的逻辑：
    // - Coordinate 是根据当前低频实体数量计算出的一个值，
    //   用于确定新墙体实体的位置。
    // - AddWall 函数会使用该坐标参数为墙体实体分配内存并初始化。
    while (WorldMode->LowEntityCount < (ArrayCount(WorldMode->LowEntities) - 16)) {
        uint32 Coordinate = 1024 + WorldMode->LowEntityCount;    // 计算墙体坐标
        AddWall(WorldMode, Coordinate, Coordinate, Coordinate);  // 添加墙体实体
    }
#endif
    // 初始化一个新的相机位置对象（NewCameraP）
    // 这个对象用于存储相机在瓦片地图中的位置
    world_position NewCameraP = {};

    // 计算相机所在的瓦片位置 X 坐标

    uint32 CameraTileX = ScreenBaseX * TilesPerWidth + 17 / 2;
    // 将屏幕的基准 X 坐标乘以每个区块的宽度，再加上偏移量（17 / 2）以定位相机的准确位置

    // 计算相机所在的瓦片位置 Y 坐标
    uint32 CameraTileY = ScreenBaseY * TilesPerHeight + 9 / 2;
    // 将屏幕的基准 Y 坐标乘以每个区块的高度，再加上偏移量（9 / 2）以定位相机的准确位置

    // 设置相机的 Z 坐标，假设 Z 坐标来自屏幕的基准 Z 坐标
    uint32 CameraTileZ = ScreenBaseZ;
    // 使用当前的基准 Z 坐标来设置相机的位置

    // 通过计算的瓦片坐标 (CameraTileX, CameraTileY, CameraTileZ) 获取世界中相应的 Chunk
    // 位置
    NewCameraP =
        ChunkPositionFromTilePosition(WorldMode->World,  // 使用世界状态对象（WorldMode->World）
                                      CameraTileX,   // 计算出的 X 坐标
                                      CameraTileY,   // 计算出的 Y 坐标
                                      CameraTileZ);  // 使用基准 Z 坐标

    WorldMode->CameraP = NewCameraP;
    GameState->WorldMode = WorldMode;
}
bool32 IsEqual(traversable_reference A, traversable_reference B) {
    bool32 Result = (ReferencesAreEqual(A.Entity, B.Entity) && (A.Index == B.Index));
    return (Result);
}
bool32 UpdateAndGameRenderWorld(game_state *GameState, game_mode_world *WorldMode,
                                transient_state *TranState, game_input *Input, game_assets *Assets,
                                render_group *RenderGroup, loaded_bitmap *DrawBuffer) {
    TIMED_FUNCTION();
    (void)Assets;
    v4 BackgroundColor = V4(0.15f, 0.15f, 0.15f, 1.0f);
#if 1
    Clear(RenderGroup, BackgroundColor);
#endif
    bool32 Result = false;
    world *World = WorldMode->World;
    real32 DistanceAboveTarget = 9.0f;

    camera_params Camera = GetStandardCameraParams(DrawBuffer->Width, 0.3f);

    v2 MouseP = {Input->MouseX, Input->MouseY};
    Perspective(RenderGroup, Camera.MetersToPixels, Camera.FocalLength, DistanceAboveTarget);

    // 确定屏幕中心点的位置，屏幕中心为DrawBuffer的宽度和高度的一半
    v2 ScreenCenter = {0.5f * (real32)DrawBuffer->Width, 0.5f * (real32)DrawBuffer->Height};
    (void)ScreenCenter;
    rectangle2 ScreenBounds = GetCameraRectangleAtTarget(RenderGroup);
    rectangle3 CameraBoundsInMeters =
        RectMinMax(ToV3(ScreenBounds.Min, 0.0f), ToV3(ScreenBounds.Max, 0.0f));
    CameraBoundsInMeters.Min.z = -3.0f * WorldMode->TypicalFloorHeight;
    CameraBoundsInMeters.Max.z = 1.0f * WorldMode->TypicalFloorHeight;

#if 1
#if 1
#endif
    bool32 HerosExist = false;

    v3 SimBoundsExpansion = v3{15.0f, 15.0f, 15.0f};
    rectangle3 SimBounds = AddRadiusTo(CameraBoundsInMeters, SimBoundsExpansion);

    temporary_memory SimMemory = BeginTemporaryMemory(&TranState->TransientArena);
    // 将相机位置赋值给世界坐标系中的SimCenterP
    world_position SimCenterP = WorldMode->CameraP;
    // 创建一个模拟区域 (SimRegion)，其中包括模拟需要的所有数据
    // BeginSim 函数初始化模拟区域，传入的参数包括：
    // - SimArena: 提供模拟所需的内存分配
    // - WorldMode: 当前游戏状态
    // - WorldMode->World: 游戏的世界数据
    // - WorldMode->CameraP: 当前相机的位置
    // - CameraBounds: 相机的边界范围，用于限定模拟区域
    sim_region *SimRegion = BeginSim(&TranState->TransientArena, WorldMode->World, SimCenterP,
                                     SimBounds, Input->dtForFrame);

    // 计算相机位置与模拟中心位置之间的差值
    // 将世界坐标系中的相机位置 (WorldMode->CameraP) 与模拟中心位置 (SimCenterP)
    // 相减，得到相对位置 (CameraP)
    v3 CameraP = Subtract(World, &WorldMode->CameraP, &SimCenterP) + WorldMode->CameraOffset;

    object_transform WorldTransform = DefaultFlatTransform();
    WorldTransform.OffsetP -= CameraP;

    PushRectOutline(RenderGroup, &WorldTransform, V3(0.0f, 0.0f, 0.0f), GetDim(ScreenBounds),
                    V4(1.0f, 1.0f, 0.0f, 1));
    // PushRectOutline(RenderGroup, V3(0.0f, 0.0f, 0.0f), GetDim(CameraBoundsInMeters).xy,
    //                 V4(1.0f, 1.0f, 1.0f, 1));
    PushRectOutline(RenderGroup, &WorldTransform, V3(0.0f, 0.0f, 0.0f), GetDim(SimBounds).xy,
                    V4(0.0f, 1.0f, 1.0f, 1));
    PushRectOutline(RenderGroup, &WorldTransform, V3(0.0f, 0.0f, 0.0f),
                    GetDim(SimRegion->Bounds).xy, V4(1.0f, 0.0f, 1.0f, 1));

    real32 dt = Input->dtForFrame;

    for (uint32 ControllerIndex = 0; ControllerIndex < ArrayCount(Input->Controllers);
         ++ControllerIndex) {
        // 获取第一个控制器的输入
        game_controller_input *Controller = GetController(Input, ControllerIndex);
        controlled_hero *ConHero = GameState->ControledHeroes + ControllerIndex;
        if (ConHero->BrainID.Value == 0) {
            if (WasPressed(Controller->Back)) {
            } else if (WasPressed(Controller->Start)) {  // 如果“开始”按钮被按下
                *ConHero = {};
                traversable_reference Traversable;
                if (GetClosestTraversable(SimRegion, CameraP, &Traversable)) {
                    HerosExist = true;
                    (void)HerosExist;
                    ConHero->BrainID = {ReservedBrainID_FirstHero + ControllerIndex};
                    AddPlayer(WorldMode, SimRegion, Traversable, ConHero->BrainID);
                } else {
                }
            }
        }
    }
    BEGIN_BLOCK("ExecuteBrain");
    for (uint32 BrainIndex = 0; BrainIndex < SimRegion->BrainCount; ++BrainIndex) {
        brain *Brain = SimRegion->Brains + BrainIndex;
        MarkBrainActives(Brain);
    }
    for (uint32 BrainIndex = 0; BrainIndex < SimRegion->BrainCount; ++BrainIndex) {
        brain *Brain = SimRegion->Brains + BrainIndex;
        ExecuteBrain(GameState, RenderGroup, Input, WorldMode, SimRegion, Brain, dt);
    }
    END_BLOCK();
#else
    (void)World;
    (void)MetersToPixels;
#endif
    BEGIN_BLOCK("SimulationEntities");
    UpdateAndRenderEntities(WorldMode, SimRegion, RenderGroup, CameraP, DrawBuffer, BackgroundColor,
                            dt, TranState, MouseP);

    WorldTransform.tColor = v4{0, 0, 0, 0};
#if 1
    TranState->EnvMaps[0].Pz = -1.0f;
    TranState->EnvMaps[1].Pz = 0.0f;
    TranState->EnvMaps[2].Pz = 1.0f;
#endif
    Orthographic(RenderGroup, DrawBuffer->Width, DrawBuffer->Height, 1.0f);
    object_transform Flat = DefaultFlatTransform();
    PushRectOutline(RenderGroup, &Flat, ToV3(MouseP, 0.0f), v2{2.0f, 2.0f}, v4{1, 0, 0, 1}, 50.0f);

    // Saturation(RenderGroup, WorldMode->Time * 0.5f + 0.5f * Sin(10.0f *
    // WorldMode->Time)); 结束模拟过程
    EndSim(SimRegion, WorldMode);

    // 结束临时内存
    EndTemporaryMemory(SimMemory);
    bool32 HeroesExist = false;
    for (uint32 ConHeroIndex = 0; ConHeroIndex < MAX_CONTROLLER_COUNT; ++ConHeroIndex) {
        if (GameState->ControledHeroes[ConHeroIndex].BrainID.Value) {
            HeroesExist = true;
            break;
        }
    }
    if (!HeroesExist) {
        PlayTitleScreen(GameState, TranState);
    }
    END_BLOCK();
    return Result;
}
#if 0

                    if (Global_Particle_ParticleTest) {
                        // NOTE:粒子系统测试
                        for (uint32 ParticleSpawnIndex = 0; ParticleSpawnIndex < 2;
                             ParticleSpawnIndex++) {
                            particle *Particle = WorldMode->Particles + WorldMode->NextParticles++;
                            if (WorldMode->NextParticles >= ArrayCount(WorldMode->Particles)) {
                                WorldMode->NextParticles = 0;
                            }
                            Particle->P = {RandomBetween(&WorldMode->EffectsEntropy, -0.05f, 0.05f),
                                           0, 0};
                            Particle->dP = {
                                RandomBetween(&WorldMode->EffectsEntropy, -0.05f, 0.05f),
                                7.0f * RandomBetween(&WorldMode->EffectsEntropy, 0.7f, 1.0f), 1.0f};
                            Particle->ddP = {0.0f, -9.8f, 0.0f};

                            Particle->Color = {
                                RandomBetween(&WorldMode->EffectsEntropy, 0.75f, 1.0f),  //
                                RandomBetween(&WorldMode->EffectsEntropy, 0.75f, 1.0f),  //
                                RandomBetween(&WorldMode->EffectsEntropy, 0.75f, 1.0f),  //
                                1.0f};
                            Particle->dColor = {0, 0, 0, -0.25f};
                            char Nothings[] = "NOTHING";
                            MatchVector.E[Tag_UnicodeCodepoint] = (real32)Nothings[RandomChoice(
                                &WorldMode->EffectsEntropy, sizeof(Nothings) - 1)];
                            WeightVector.E[Tag_UnicodeCodepoint] = 1.0f;
                            Particle->BitmapID = HeroBitmaps.Head;
                            // Particle->BitmapID = GetBestMatchBitmapFrom(TranState->Assets,
                            // Asset_Font,
                            //                                             &MatchVector,
                            //                                             &WeightVector);
                            // Particle->BitmapID = GetRandomBitmapFrom(TranState->Assets,
                            // Asset_Font,
                            //                                          &WorldMode->EffectsEntropy);
                        }
                        ZeroStruct(WorldMode->ParticleCels);
                        real32 GridScale = 0.25f;
                        real32 InvGridScale = 1.0f / GridScale;
                        v3 GridOrigin = {-0.5f * GridScale * PARTICLE_CEL_DIM, 0.0f, 0.0f};
                        for (uint32 ParticleIndex = 0;
                             ParticleIndex < ArrayCount(WorldMode->Particles); ++ParticleIndex) {
                            particle *Particle = WorldMode->Particles + ParticleIndex;
                            v3 P = InvGridScale * (Particle->P - GridOrigin);
                            uint32 X = TruncateReal32ToUInt32(P.x);
                            uint32 Y = TruncateReal32ToUInt32(P.y);
                            if (X < 0) {
                                X = 0;
                            };
                            if (X > (PARTICLE_CEL_DIM - 1)) {
                                X = (PARTICLE_CEL_DIM - 1);
                            }
                            if (Y < 0) {
                                Y = 0;
                            };
                            if (Y > (PARTICLE_CEL_DIM - 1)) {
                                Y = (PARTICLE_CEL_DIM - 1);
                            }
                            particle_cel *Cel = &WorldMode->ParticleCels[Y][X];
                            real32 Density = Particle->Color.a;
                            Cel->Density += Density;
                            Cel->VelocityTimesDensity += Density * Particle->dP;
                        }
                        if (Global_Particle_ParticleGrid) {
                            for (uint32 Y = 0; Y < PARTICLE_CEL_DIM; Y++) {
                                for (uint32 X = 0; X < PARTICLE_CEL_DIM; X++) {
                                    particle_cel *Cel = &WorldMode->ParticleCels[Y][X];
                                    real32 Alpha = Clamp01(0.1f * Cel->Density);
                                    PushRect(
                                        RenderGroup, EntityTransform,
                                        GridScale * v3{(real32)X, (real32)Y, 0.0f} + GridOrigin,
                                        GridScale * v2{1.0, 1.0f}, v4{Alpha, Alpha, Alpha, Alpha});
                                }
                            }
                        }
                        for (uint32 ParticleIndex = 0;
                             ParticleIndex < ArrayCount(WorldMode->Particles); ++ParticleIndex) {
                            particle *Particle = WorldMode->Particles + ParticleIndex;
                            v3 P = InvGridScale * (Particle->P - GridOrigin);
                            uint32 X = TruncateReal32ToUInt32(P.x);
                            uint32 Y = TruncateReal32ToUInt32(P.y);
                            if (X < 1) {
                                X = 1;
                            };
                            if (X > (PARTICLE_CEL_DIM - 2)) {
                                X = (PARTICLE_CEL_DIM - 2);
                            }
                            if (Y < 1) {
                                Y = 1;
                            };
                            if (Y > (PARTICLE_CEL_DIM - 2)) {
                                Y = (PARTICLE_CEL_DIM - 2);
                            }
                            particle_cel *CelCenter = &WorldMode->ParticleCels[Y][X];
                            particle_cel *CelLeft = &WorldMode->ParticleCels[Y][X - 1];
                            particle_cel *CelRight = &WorldMode->ParticleCels[Y][X + 1];
                            particle_cel *CelDown = &WorldMode->ParticleCels[Y - 1][X];
                            particle_cel *CelUp = &WorldMode->ParticleCels[Y + 1][X];
                            v3 Dispersion = {};
                            real32 Dc = 1.0f;
                            Dispersion += Dc * (CelCenter->Density - CelLeft->Density) *
                                          v3{-1.0f, 0.0f, 0.0f};
                            Dispersion += Dc * (CelCenter->Density - CelRight->Density) *
                                          v3{1.0f, 0.0f, 0.0f};
                            Dispersion +=
                                Dc * (CelCenter->Density - CelUp->Density) * v3{0.0f, -1.0f, 0.0f};
                            Dispersion +=
                                Dc * (CelCenter->Density - CelDown->Density) * v3{0.0f, 1.0f, 0.0f};

                            ddP = Particle->ddP + Dispersion;
                            Particle->P += 0.5f * Square(Input->dtForFrame) * ddP +
                                           Input->dtForFrame * Particle->dP;
                            Particle->dP += Input->dtForFrame * ddP;
                            Particle->Color += Input->dtForFrame * Particle->dColor;
                            if (Particle->P.y < 0) {
                                real32 CeofficientOfRestitution = 0.5f;
                                real32 CeofficientOfFriction = 0.7f;
                                Particle->P.y = -Particle->P.y;
                                Particle->dP.y = -CeofficientOfRestitution * Particle->dP.y;
                                Particle->dP.x = CeofficientOfFriction * Particle->dP.x;
                            }

                            v4 Color;
                            Color.r = Clamp01(Particle->Color.r);
                            Color.g = Clamp01(Particle->Color.g);
                            Color.b = Clamp01(Particle->Color.b);
                            Color.a = Clamp01(Particle->Color.a);

                            if (Color.a > 0.9f) {
                                Color.a = 0.9f * ClampMapToRange(1.0f, Color.a, 0.9f);
                            }
                            PushBitmap(RenderGroup, EntityTransform, Particle->BitmapID, 1.0f,
                                       Particle->P, Color /*Particle->Color*/);
                        }
                    }
#endif